﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace AiFrame.InterfaceLib.Data.Patterns
{
    /// <summary>
    /// Acts as a wrapper class which saves the property path.
    /// </summary>
    public class SortExpressionWrapper
    {
        public SortExpressionWrapper(string propertyPath)
        {
            this.PropertyPath = propertyPath;
        }

        public string PropertyPath { get; set; }
    }

    /// <summary>
    /// Represents the sort direction.
    /// </summary>
    public enum SortDirection
    {
        Descending,
        Ascending
    }

    public class ListSorter
    {
        /// <summary>
        /// Sorts a list.
        /// </summary>
        /// <typeparam name="T">The type of the item stored in the list.</typeparam>
        /// <param name="list">The list which has to be sorted.</param>
        /// <param name="sortExpression">The path to the property.</param>
        /// <param name="sortDirection">The sort direction</param>
        /// <param name="virtualQuery">The virtual query.</param>
        public static void Sort<T>(ISortableList<T> list, string sortExpression, SortDirection sortDirection, IVirtualQuery virtualQuery)
        {
            string[] sortExpressions = sortExpression.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);

            List<GenericComparer> comparers = new List<GenericComparer>();

            Type type = typeof (T);
            
            for (int i = 0; i < sortExpressions.Count(); i++)
            {
                string sortExpress = sortExpressions[i];
                
                /* Wenn der letzte Index erreicht ist... */
                if (i == sortExpressions.Count() - 1)
                {
                    /* Wenn der letzte Index erreicht ist, muss die Property
                     * von der Sortierrichtung abgeschnitten werden */
                    string sortProperty = sortExpress.Trim().Split(' ')[0].Trim();

                    PropertyInfo propertyInfo = type.GetProperty(sortProperty);

                    if (propertyInfo == null)
                    {
                        throw new Exception(String.Format("{0} is not a valid property of type: \"{1}\"",
                                                          sortProperty, type.Name));
                    }

                    comparers.Add(new GenericComparer
                    {
                        SortDirection = sortDirection,
                        SortExpressions = sortExpressions,
                        comparers = comparers,
                        VirtualQuery = virtualQuery
                    });
                }
                else
                {
                    string sortProperty = sortExpress.Trim();
                    
                    PropertyInfo propertyInfo = type.GetProperty(sortProperty);

                    if (propertyInfo == null)
                    {
                        throw new Exception(String.Format("{0} is not a valid property of type: \"{1}\"",
                                                          sortProperty, type.Name));
                    }
                    /* Den Typ für den nächsten Durchlauf abspeichern */
                    type = propertyInfo.PropertyType;
                }
            }
            
            list.Sort(comparers[0].Compare);
        }
    }

    /// <summary>
    /// A generic comparer
    /// </summary>
    public class GenericComparer
    {
        public List<GenericComparer> comparers { get; set; }
        int level = 0;

        public SortDirection SortDirection { get; set; }
        public string[] SortExpressions { get; set; }

        public IVirtualQuery VirtualQuery { get; set; }

        public int Compare<T>(T t1, T t2)
        {
            int ret = 0;
            
            if (level >= comparers.Count)
                return 0;
            
            /* Anhand des Pfades zur Property gehen */
            Type type = typeof (T);
            PropertyInfo propertyInfo = null;
            object t1Value = t1;
            object t2Value = t2;
            for(int i = 0; i < SortExpressions.Count(); i++)
            {
                propertyInfo = type.GetProperty(SortExpressions[i].Trim());
                t1Value = propertyInfo.GetValue(t1Value, null);
                t2Value = propertyInfo.GetValue(t2Value, null);
                type = propertyInfo.PropertyType;
            }

            /* Prüfen, ob die VirtualQuery benutzt werden soll */
            if (VirtualQuery != null)
            {
                t1Value = VirtualQuery.QueryProperty(propertyInfo.Name, t1Value);
                t2Value = VirtualQuery.QueryProperty(propertyInfo.Name, t2Value);
            }
            
            if (t1 == null || t1Value == null)
            {
                if (t2 == null || t2Value == null)
                {
                    ret = 0;
                }
                else
                {
                    ret = -1;
                }
            }
            else
            {
                if (t2 == null || t2Value == null)
                {
                    ret = 1;
                }
                else
                {
                    ret = ((IComparable)t1Value).CompareTo(((IComparable)t2Value));
                }
            }
            if (ret == 0)
            {
                level += 1;
                ret = Compare(t1, t2);
                level -= 1;
            }
            else
            {
                if (comparers[level].SortDirection == SortDirection.Descending)
                {
                    ret *= -1;
                }
            }
            return ret;
        }
    }
}
